// 12.3.3 非类型模板实参
// 非类型模板实参（Non-type Template Arguments）是传递给模板的具体值，而非类型。这些值在编译时必须是常量表达式，用于实例化模板。

// 基本概念
// 非类型模板实参必须是编译时常量，可以传递给相应类型的非类型模板参数：
template <typename T, int Size>
class Array
{
    T elements[Size];
};

// 使用整型常量作为非类型实参
Array<double, 10> doubleArray;

// 允许的非类型实参
// C++标准规定了可以用作非类型实参的值类型：
// 1.整型和枚举常量：
template <int N>
class IntConstant
{
};

const int c = 42;
IntConstant<42> a;    // 字面量
IntConstant<c> b;     // 常量变量
IntConstant<1 + 2> c; // 常量表达式

// 2.指针：
template <int *P>
class PointerConstant
{
};

int global = 42;
PointerConstant<&global> p; // 全局变量的地址

// 3.引用：
template <int &R>
class ReferenceConstant
{
};

extern int external;
ReferenceConstant<external> r; // 外部变量的引用

// 4.成员指针：
template <int Class::*PMD>
class MemberPointerConstant
{
};

struct Class
{
    int member;
};
MemberPointerConstant<&Class::member> mp;

// 5.nullptr（C++11起）：
template <decltype(nullptr) N>
class NullptrConstant
{
};

NullptrConstant<nullptr> n;

// 非类型实参的限制
// 非类型实参有一些重要限制：
// 1.必须是编译时常量：
int var = 42;
// Array<double, var> error;  // 错误：var不是编译时常量

// 2.不能使用浮点数（C++20之前）：
template <double D> // C++20之前错误
class DoubleConstant
{
};

// 3.指针和引用的限制：
// 必须指向具有外部链接或静态存储期的对象
// 不能是字符串字面量或临时对象
template <const char *Str>
class StringConstant
{
};

// StringConstant<"hello"> error;  // C++20之前错误

// 4.表达式限制：
// 必须是编译时可求值的表达式
// 不能调用普通函数（只能调用constexpr函数）

